package com.usthe.bootshiro.shiro.config;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanInitializationException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

/**
 *   rest支持的shiroFilterFactoryBean
 * @author tomsun28
 * @date 21:35 2018/4/20
 */
public class RestShiroFilterFactoryBean extends ShiroFilterFactoryBean {

    private static final Logger LOGGER = LoggerFactory.getLogger(RestShiroFilterFactoryBean.class);

    public RestShiroFilterFactoryBean() {
        super();
    }

    /**
     * 一、创建了一个过滤器管理类FilterChainManager
     * filters:管理全部过滤器,包括默认的关于身份验证和权限验证的过滤器，这些过滤器分为两组，
     *  一组是认证过滤器，有anon，authcBasic，auchc，user，
     *  一组是授权过滤器，有perms，roles，ssl，rest，port。
     *  同时也包含在xml里filters配置的自定义过滤器。在其它地方使用时都是从过滤器管理类里filters里拿的。
     *  且过滤器是单例的，整个Shiro框架只维护每种类型过滤器的单例。
     *
     *
     * filterChains:过滤链。
     *  它是我们重点关注的东西，是一个Map对象，其中key就是我们请求的url,value是一个NamedFilterList对象，
     *  里面存放的是与url对应的一系列过滤器。
     *
     *
     *  {
     *     /login.jsp=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@6ed97422,
     *     /login=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@592dbd8,
     *     /logout=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@1141badb,
     *     /authenticated=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@2d1b876e,
     *     /views/**=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@31106774,
     *     /**=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@14cd7d10,
     *     /index.jsp=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@712384a,
     *     /admin.jsp=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@7643ef31
     * }
     *
     *
     *  二、将过滤器管理类设置到PathMatchingFilterChainResolver类里，该类负责路径和过滤器链的解析与匹配。根据url找到过滤器链。
     */
    @Override
    protected AbstractShiroFilter createInstance() throws Exception {
        LOGGER.debug("Creating Shiro Filter instance.");
        SecurityManager securityManager = this.getSecurityManager();
        String msg;
        if (securityManager == null) {
            msg = "SecurityManager property must be set.";
            throw new BeanInitializationException(msg);
        } else if (!(securityManager instanceof WebSecurityManager)) {
            msg = "The security manager does not implement the WebSecurityManager interface.";
            throw new BeanInitializationException(msg);
        } else {
            //创建过滤器管理类【filters+filterChains】
            FilterChainManager manager = this.createFilterChainManager();
            //设置-根据url找到过滤器链
            //当有请求过来里，shiro会通过PathMatchingFilterChainResolver解析得到请求的url对应的过滤器链。
            RestPathMatchingFilterChainResolver chainResolver = new RestPathMatchingFilterChainResolver();
            chainResolver.setFilterChainManager(manager);
            return new RestShiroFilterFactoryBean.SpringShiroFilter((WebSecurityManager)securityManager, chainResolver);
        }
    }

    /**
     *
     */
    private static final class SpringShiroFilter extends AbstractShiroFilter {
        protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
            if (webSecurityManager == null) {
                throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
            } else {
                this.setSecurityManager(webSecurityManager);
                if (resolver != null) {
                    this.setFilterChainResolver(resolver);
                }

            }
        }
/*
一个请求过来时，先到达AbstractShiroFilter.executeChain()方法，去根据request解析出来的url找到对应的过滤链，然后执行过滤器链。
 */
        protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)
                throws IOException, ServletException {
            FilterChain chain = getExecutionChain(request, response, origChain);
            chain.doFilter(request, response);
        }


        protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
            FilterChain chain = origChain;

            FilterChainResolver resolver = getFilterChainResolver();
            if (resolver == null) {
                //log.debug("No FilterChainResolver configured.  Returning original FilterChain.");
                return origChain;
            }
             //根据解析出来的requestURI找到对应的过滤器链
            FilterChain resolved = resolver.getChain(request, response, origChain);
            if (resolved != null) {
                //log.trace("Resolved a configured FilterChain for the current request.");
                chain = resolved;
            } else {
                //log.trace("No FilterChain configured for the current request.  Using the default.");
            }

            return chain;
        }


    }

}
